using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace CodeGenTest
{
    public abstract class Expr
    {
        public abstract void Eval(ILGenerator ilg);
    }

    public class AddOp : Expr
    {
        private Expr leftSide;
        private Expr rightSide;
        

        public AddOp(Expr leftSide, Expr rightSide)
        {
            this.leftSide = leftSide;
            this.rightSide = rightSide;
        }

        public override void Eval(ILGenerator ilg)
        {
            leftSide.Eval(ilg);
            rightSide.Eval(ilg);
            ilg.Emit(OpCodes.Add);
        }

        public override string ToString()
        {
            return "(" + leftSide + "+" + rightSide + ")";
        }
    }

    public class MultiPly : Expr
    {
        private Expr leftSide;
        private Expr rightSide;


        public MultiPly(Expr leftSide, Expr rightSide)
        {
            this.leftSide = leftSide;
            this.rightSide = rightSide;
        }

        public override void Eval(ILGenerator ilg)
        {
            leftSide.Eval(ilg);
            rightSide.Eval(ilg);
            ilg.Emit(OpCodes.Mul);
        }

        public override string ToString()
        {
            return leftSide + "*" + rightSide;
        }
    }

    public class Value : Expr
    {
        private int val;

        public Value(int val)
        {
            this.val = val;
        }

        public override void Eval(ILGenerator ilg)
        {
            ilg.Emit(OpCodes.Ldc_I4, val);
        }

        public override string ToString()
        {
            return val.ToString();
        }
    }

    public class DeclareVariable<T> : Expr
    {
        private Variable var;

        public DeclareVariable(Variable var)
        {
            this.var = var;
        }

        public override void Eval(ILGenerator ilg)
        {
            var.Var = ilg.DeclareLocal(typeof(T)); 
        }

        public override string ToString()
        {
            return typeof (T).ToString() + " " + var.VariableName + ";";
        }
    }

    public class SetVariable : Expr
    {
        private Expr value;
        private Variable var;


        public SetVariable(Expr value, Variable var)
        {
            this.value = value;
            this.var = var;
        }

        public override void Eval(ILGenerator ilg)
        {
            value.Eval(ilg);
            ilg.Emit(OpCodes.Stloc, var.Var);
        }

        public override string ToString()
        {
            return var.VariableName + " = " + value;
        }
    }

    public class GetVariable : Expr
    {
        private Variable var;

        public GetVariable(Variable var)
        {
            this.var = var;
        }


        public override void Eval(ILGenerator ilg)
        {
            ilg.Emit(OpCodes.Ldloc, var.Var);
        }

        public override string ToString()
        {
            return var.VariableName;
        }
    }

    public class RandomValue : Expr
    {
        public static Random random = new Random();
        public static Random rnd
        {
            get{return random;}        
        }

        public RandomValue()
        {
            random = new Random();
        }

        public override void Eval(ILGenerator ilg)
        {
            ilg.Emit(OpCodes.Ldsfld, typeof(RandomValue).GetField("random"));

            ilg.EmitCall(OpCodes.Callvirt, typeof(System.Random).GetMethod("NextDouble"), null);
            
            ilg.Emit(OpCodes.Ldc_R8, 100.0);
            ilg.Emit(OpCodes.Mul);
            ilg.Emit(OpCodes.Conv_I4);
            
        }

        public override string ToString()
        {
            return "Rand()";
        }
    }

    public class ProgramLines : Expr
    {
        private List<Expr> programList;
        
        public ProgramLines()
        {
            programList = new List<Expr>();
        }

        public void AddLine(Expr e)
        {
            programList.Add(e);
        }


        public override void Eval(ILGenerator ilg)
        {
            foreach (Expr expr in programList)
            {
                expr.Eval(ilg);
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < programList.Count; i++)
            {
                sb.Append(i.ToString()).Append("0: ").AppendLine(programList[i].ToString());
            }
            return sb.ToString();
        }


    }

    /*public class RandomWrapper
    {
        private Random random;


        public RandomWrapper()
        {
            this.random = new Random();
        }

        public int Next()
        {
            return random.Next();
        }
    }*/

}
